﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Sockets;

namespace LuceneApiServerLib
{
    public class Option
    {
        public bool Delfromindex = false;
        public string FromName;
        public string[] Ids;
        public string IndexName;
        public LnModel[] Models;
        public string UserName;
        public string KeyWord;
    }

    public enum Optionenum
    {
        SetIndex,
        DeleteIndex,
        DeleteIndexAll,
        OptimizeIndex,
        Merge
    }

    /// <summary>
    ///     表示Lucene操作对象
    ///     不可继承
    /// </summary>
    /// <typeparam name="T">模型类型</typeparam>
    [DebuggerDisplay("IndexName ={indexName}")]
    public sealed class Lucene<T> : IDisposable
    {
        /// <summary>
        ///     索引名称
        /// </summary>
      //  private readonly string indexName;

        private Option defOption;
        /// <summary>
        ///     模型的属性
        /// </summary>
        private readonly LnProperty[] _properties;

        private readonly string url;

        /// <summary>
        ///     是否已释放
        /// </summary>
        private bool isDisposed;

        /// <summary>
        ///     Lucene操作对象
        /// </summary>
        /// <param name="indexName">索引名称</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentException"></exception>
        public Lucene(string indexName, string linkurl, string userName="", string KeyWord="")
        {
            if (string.IsNullOrEmpty(indexName))
            {
                throw new ArgumentNullException("indexName");
            }
            if (string.IsNullOrEmpty(linkurl))
            {
                throw new ArgumentNullException("linkurl");
            }
            defOption = new Option
            {
                IndexName = indexName,
                UserName = userName,
                KeyWord=KeyWord
            };
            url = linkurl;
            var properties = typeof (T)
                .GetProperties()
                .Where(p => p.CanRead && p.CanWrite)
                .Where(p => IsSupportedType(p.PropertyType))
                .Where(p => p.IsDefined(typeof (FieldAttribute), false));

            if (properties.Any(p => string.Equals(p.Name, "Id", StringComparison.OrdinalIgnoreCase)) == false)
            {
                throw new ArgumentException("类型" + typeof (T).Name + "必须声明get和set的Id属性，表示唯一标识符");
            }

          //  this.indexName = indexName;
            this._properties = properties.Select(p => new LnProperty(p)).ToArray();
        }

        /// <summary>
        ///     释放资源
        /// </summary>
        public void Dispose()
        {
            if (isDisposed == false)
            {
                isDisposed = true;
            }
        }

        /// <summary>
        ///     客户端
        /// </summary>
        /// <summary>
        ///     类型是否为所支持的简单类型
        /// </summary>
        /// <param name="type">类型</param>
        /// <returns></returns>
        private bool IsSupportedType(Type type)
        {
            if (typeof (IConvertible).IsAssignableFrom(type))
            {
                return true;
            }

            if (typeof (Guid) == type)
            {
                return true;
            }

            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof (Nullable<>))
            {
                var argTypes = type.GetGenericArguments();
                if (argTypes.Length == 1)
                {
                    return IsSupportedType(argTypes.First());
                }
            }
            return false;
        }

        /// <summary>
        ///     将业务模型转换为LN模型
        ///     用for以支持多线程遍历
        /// </summary>
        /// <param name="model">业务模型</param>
        /// <returns></returns>
        private LnModel TModelToLnModel(T model)
        {
            var fields = new LnField[_properties.Length];
            for (var i = 0; i < _properties.Length; i++)
            {
                var property = _properties[i];
                fields[i] = new LnField
                {
                    Name = property.Name,
                    Value = property.GetValue(model),
                    Index = property.Index,
                    Store = property.Store
                };
            }
            return new LnModel {Fields = fields};
        }

        /// <summary>
        ///     创建或更新到索引
        /// </summary>
        /// <param name="model">模型</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="SocketException"></exception>
        /// <returns></returns>
        public bool SetIndex(T model)
        {
            if (model == null)
            {
                throw new ArgumentNullException("model");
            }
            return SetIndex(new[] {model});
        }

        /// <summary>
        ///     创建或更新到索引
        /// </summary>
        /// <param name="models">模型</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="SocketException"></exception>
        /// <exception cref="SerializerException"></exception>
        /// <returns></returns>
        public bool SetIndex(IEnumerable<T> models)
        {
            if (models == null)
            {
                throw new ArgumentNullException("models");
            }
            var lnModels = models.Select(TModelToLnModel);
            
            return GetWebUtil.PostGetJson<bool>(url, new Dictionary<string, string>
            {
                {
                    "doOption", Optionenum.SetIndex.ToString()
                }
            }, new Option
            {
                IndexName = defOption.IndexName,
                UserName = defOption.UserName,
                KeyWord = defOption.KeyWord,
                Models = lnModels.ToArray()
            });
        }

        /// <summary>
        ///     删除索引的全部记录
        /// </summary>
        /// <exception cref="SocketException"></exception>
        /// <returns></returns>
        public bool DeleteIndex()
        {
            return GetWebUtil.PostGetJson<bool>(url, new Dictionary<string, string>
            {
                {
                    "doOption", Optionenum.DeleteIndexAll.ToString()
                }
            }, defOption);
        }

        /// <summary>
        ///     删除索引的记录
        /// </summary>
        /// <param name="id">记录的id值</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="SocketException"></exception>
        /// <returns></returns>
        public bool DeleteIndex(string id)
        {
            if (id == null)
            {
                throw new ArgumentNullException("id");
            }
            return GetWebUtil.PostGetJson<bool>(url, new Dictionary<string, string>
            {
                {
                    "doOption", Optionenum.DeleteIndex.ToString()
                }
            }, new Option
            {
                IndexName = defOption.IndexName,
                UserName = defOption.UserName,
                KeyWord = defOption.KeyWord,
                Ids = new[] {id}
            });
        }

        /// <summary>
        ///     删除索引
        /// </summary>
        /// <param name="ids">id值</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="SocketException"></exception>
        /// <returns></returns>
        public bool DeleteIndex(IEnumerable<string> ids)
        {
            if (ids == null)
            {
                throw new ArgumentNullException("ids");
            }
            var idValues = ids.Select(item => item.ToString()).ToArray();
            return GetWebUtil.PostGetJson<bool>(url, new Dictionary<string, string>
            {
                {
                    "doOption", Optionenum.DeleteIndex.ToString()
                }
            }, new Option
            {
                IndexName = defOption.IndexName,
                UserName = defOption.UserName,
                KeyWord = defOption.KeyWord,
                Ids = idValues
            });
        }

        /// <summary>
        ///     优化索引
        /// </summary>
        /// <returns></returns>
        public bool OptimizeIndex()
        {
            return GetWebUtil.PostGetJson<bool>(url, new Dictionary<string, string>
            {
                {
                    "doOption", Optionenum.OptimizeIndex.ToString()
                }
            }, defOption);
        }

        /// <summary>
        ///     索引搜索
        /// </summary>
        /// <param name="keywords">关键字</param>
        /// <returns></returns>
        public LnQuery<T> SearchIndex(string keywords)
        {
            return new LnQuery<T>(url, _properties, defOption.IndexName, keywords);
        }
    }
}